In [2]:
%pylab
from pandas import Series, DataFrame
import pandas as pd
import re
In [3]:
# pandas 的 merge() 方法
df1 = DataFrame({'key': list('bbacaabd'),
'data1': range(8)})
df2 = DataFrame({'key': list('abd'),
'data2': range(3)})
In [4]:
df1
Out[4]:
In [5]:
df2
Out[5]:
In [6]:
# 用 merge() 方法,預設以共同的column 'key' 來 join 兩個 DataFrame
pd.merge(df1, df2)
Out[6]:
In [7]:
# 也可以顯式的指定 要以哪一個欄位來 join
pd.merge(df1, df2, on = 'key')
Out[7]:
In [8]:
# 如果兩個物件的列名不同,也可以分別指定
df3 = DataFrame({'lkey': list('bbacaabd'),
'data1': range(8)})
df4 = DataFrame({'rkey': list('abd'),
'data2': range(3)})
In [9]:
df3
Out[9]:
In [10]:
df4
Out[10]:
In [11]:
# 分別指定各要以哪一個欄位來join
pd.merge(df3, df4, left_on = 'lkey', right_on = 'rkey')
# lkey = 'c' 的項目不會出現,因為 merge()方法預設以 inner join 的模式來merge
Out[11]:
In [12]:
# 可以以 how 參數指定 join的模式 (outer 聯集)
pd.merge(df3, df4, left_on = 'lkey', right_on = 'rkey', how = 'outer')
Out[12]:
In [13]:
# 可以以 how 參數指定 join的模式 (inner)
pd.merge(df3, df4, left_on = 'lkey', right_on = 'rkey', how = 'inner')
Out[13]:
In [14]:
# 可以以 how 參數指定 join的模式 (left)
pd.merge(df3, df4, left_on = 'lkey', right_on = 'rkey', how = 'left')
Out[14]:
In [15]:
# 可以以 how 參數指定 join的模式 (right)
pd.merge(df3, df4, left_on = 'lkey', right_on = 'rkey', how = 'right')
Out[15]:
In [16]:
# 多對多的合併
df1 = DataFrame({'key': list('bbacab'),
'data1': range(6)})
df2 = DataFrame({'key': list('ababd'),
'data2': range(5)})
In [17]:
df1
Out[17]:
In [18]:
df2
Out[18]:
In [19]:
# 因為 df2中 key a, b 都有對應多個值,所以會產生多個對應 rows
pd.merge(df1, df2, on = 'key', how = 'left')
Out[19]:
In [20]:
# 因為 df2中 key a, b 都有對應多個值,所以會產生多個對應 rows
pd.merge(df1, df2, on = 'key', how = 'inner')
# inner join 是求交集,所以不會有 NaN的值出現
Out[20]:
In [21]:
# 可以根據多個 keys來 join
df1 = DataFrame({'key1': ['foo', 'foo', 'bar'],
'key2': ['one', 'two', 'one'],
'data': [1, 2, 3]})
df2 = DataFrame({'key1': ['foo', 'foo', 'bar', 'bar'],
'key2': ['one', 'one', 'one', 'two'],
'data': [4, 5, 6, 7]})
In [22]:
df1
Out[22]:
In [23]:
df2
Out[23]:
In [24]:
# 可以根據多個 keys來 join
pd.merge(df1, df2, on = ['key1', 'key2'], how = 'outer')
Out[24]:
In [25]:
# 如果列名重複 ('key2')
pd.merge(df1, df2, on = ['key1'], how = 'outer')
Out[25]:
In [26]:
# 如果列名重複 ('key2')
# 使用 suffix 參數來指定附加到左右兩個 DataFrame重複列名的名稱上
pd.merge(df1, df2, on = ['key1'], how = 'outer', suffixes = ['_left', '_right'])
Out[26]:
In [27]:
# 設定 sort 參數, 依據 keys來排序
pd.merge(df1, df2, on = ['key1', 'key2'], how = 'outer', suffixes = ['_left', '_right'], sort = True)
Out[27]:
In [28]:
# 使用DataFrame的索引作為 join的 key
dfl = DataFrame({'key': list('abaabc'),
'value': range(6)})
# dfr 以 ['a', 'b'] 作為索引
dfr = DataFrame({'value': [3.5, 7]}, index = list('ab'))
In [29]:
dfl
Out[29]:
In [30]:
# dfr 以 ['a', 'b'] 作為索引
dfr
Out[30]:
In [31]:
# 設定 right_index = True,表示 right DataFrame使用索引作為 join 的 key欄位
pd.merge(dfl, dfr, left_on = 'key', right_index = True, how = 'outer', suffixes = ['_left', '_right'], sort = True)
Out[31]:
In [32]:
# 階層化索引
dfl = DataFrame({'key1': ['Ohio', 'Ohio', 'Ohio', 'Nevada', 'Nevada'],
'key2': [2000, 2001, 2002, 2001, 2002],
'data': np.arange(5.)})
dfr = DataFrame(np.arange(12).reshape((6, 2)),
columns = ['data1', 'data2'],
index = [['Nevada', 'Nevada', 'Ohio', 'Ohio', 'Ohio', 'Ohio'],
[2001, 2000, 2000, 2000, 2001, 2002]])
dfr.index.names = ['state', 'year']
In [33]:
dfl
Out[33]:
In [34]:
dfr
Out[34]:
In [35]:
# 階層化索引
# 左方指定用來join的 keys: left_on = ['key1', 'key2']
# 右方指定使用索引來做為 join的 keys: right_index = True
pd.merge(dfl, dfr, left_on = ['key1', 'key2'], right_index = True, how = 'outer', suffixes = ['_left', '_right'], sort = True)
Out[35]:
In [36]:
# 同時使用合併雙方的索引
dfl = DataFrame([[1., 2.], [3., 4.], [5., 6.]],
index = list('ace'),
columns = ['Ohio', 'Nevada'])
dfr = DataFrame([[7., 8.], [9., 10.], [11., 12.], [13., 14]],
index = list('bcde'),
columns = ['Missouri', 'Alabama'])
In [37]:
dfl
Out[37]:
In [38]:
dfr
Out[38]:
In [39]:
pd.merge(dfl, dfr, left_index = True, right_index = True, how = 'outer', suffixes = ['_left', '_right'], sort = True )
Out[39]:
In [40]:
# NumPy中有 concatenate()方法
arr = np.arange(12).reshape((3, 4))
arr
Out[40]:
In [41]:
# NumPy 的 concatenate()方法
np.concatenate([arr, arr], axis = 1)
Out[41]:
In [42]:
# pandas 有 concat()方法
s1 = Series([0, 1], index = ['a', 'b'])
s2 = Series([2, 3, 4], index = ['c', 'd', 'e'])
s3 = Series([5, 6], index = ['f', 'g'])
sc = pd.concat([s1, s2, s3])
sc
Out[42]:
In [43]:
type(sc)
Out[43]:
In [44]:
# concat()方法預設以 axis = 0 來連接,如果傳入 axis = 1,則會產生一個 DataFrame
sc = pd.concat([s1, s2, s3], axis = 1)
sc
Out[44]:
In [45]:
type(sc)
Out[45]:
In [46]:
# 傳入 join = 'inner' 可以看到交集
s4 = pd.concat([s1 * 5, s3])
s4
Out[46]:
In [47]:
s1
Out[47]:
In [48]:
pd.concat([s1, s4], axis = 1)
Out[48]:
In [49]:
# 傳入 join = 'inner' 可以看到交集
sc = pd.concat([s1, s4], axis = 1, join = 'inner')
sc
Out[49]:
In [50]:
# 透過 join_axes 參數,指定要在其他軸上使用的索引
pd.concat([s1, s4], axis = 1, join_axes = [['a', 'c', 'b', 'e']])
Out[50]:
In [51]:
s3
Out[51]:
In [52]:
# 使用 keys 參數,建立 階層式索引
result = pd.concat([s1, s1, s3], keys = ['one', 'two', 'three'])
result
Out[52]:
In [53]:
# 把具有層次化索引的 Series, unstack 成為 DataFrame
result.unstack()
Out[53]:
In [54]:
# 沿著 axis = 1 做 concat,keys就會成為 列頭
result = pd.concat([s1, s1, s3], axis = 1, keys = ['one', 'two', 'three'])
result
Out[54]:
In [55]:
# 同樣的邏輯對 DataFrame也是一樣的
# 沿著 axis = 1 做 concat,keys就會成為 列頭
df1 = DataFrame(np.arange(6).reshape((3, 2)),
index = ['a', 'b', 'c'],
columns = ['one', 'two'])
df2 = DataFrame(5 + np.arange(4).reshape((2, 2)),
index = ['a', 'c'],
columns = ['three', 'four'])
pd.concat([df1, df2], axis = 1, keys = ['level1', 'level2'])
Out[55]:
In [56]:
# 傳入一個字典,則字典的鍵就會被當作keys參數的值
# 這種表達方式比較容易讀懂
pd.concat({'level1': df1, 'level2': df2}, axis = 1)
Out[56]:
In [57]:
# names 參數,設定層次化所引的名稱
pd.concat({'level1': df1, 'level2': df2}, axis = 1, names = ['upper', 'lower'])
Out[57]:
In [58]:
# 和當下分析工作無關的row索引
df1 = DataFrame(np.random.randn(3, 4), columns = list('abcd'))
df2 = DataFrame(np.random.randn(2, 3), columns = list('bda'))
In [59]:
df1
Out[59]:
In [60]:
df2
Out[60]:
In [61]:
# concat之後,會保留原來的索引
pd.concat([df1, df2])
Out[61]:
In [62]:
# ignore_index = True,不保留原本的索引
pd.concat([df1, df2], ignore_index = True)
Out[62]:
In [63]:
# NumPy的 where 函數
a = Series([np.nan, 2.5, np.nan, 3.5, 4.5, np.nan],
index = list('abcdef'))
b = Series(np.arange(len(a)), dtype = np.float64,
index = list('abcdef'))
In [64]:
a
Out[64]:
In [65]:
b[-1] = np.nan
In [66]:
b
Out[66]:
In [67]:
# NumPy的 where 函數,是一種向量化的 if-else
np.where(pd.isnull(a), b, a)
Out[67]:
In [68]:
b[:-2]
Out[68]:
In [69]:
a[2:]
Out[69]:
In [70]:
# Series 的 combine_first()方法,也是一樣的功能,且會自動對齊數據
b[:-2].combine_first(a[2:])
Out[70]:
In [71]:
# 對於DataFrame,combine_first的功能就像是在對缺失數據 打補釘
df1 = DataFrame({'a': [1., np.nan, 5., np.nan],
'b': [np.nan, 2., np.nan, 5.],
'c': list(range(2, 18, 4))})
df2 = DataFrame({'a': [5., 4., np.nan, 3., 7.],
'b': [np.nan, 3., 4., 6., 8.]})
In [72]:
df1
Out[72]:
In [73]:
df2
Out[73]:
In [74]:
# 對於 df1中的缺失數據,會嘗試以df2中的對應數據補充
df1.combine_first(df2)
Out[74]:
In [75]:
# 主要兩種方法
# stack: 將 column 旋轉為 row
# unstack: 將 row 旋轉為 column
In [76]:
df = DataFrame(np.arange(6).reshape((2, 3)),
index = pd.Index(['Ohio', 'Colorado'], name = 'state'),
columns = pd.Index(['one', 'two', 'three'], name = 'number'))
df
Out[76]:
In [77]:
# stack: 將 column 旋轉為 row
s = df.stack()
s
Out[77]:
In [78]:
# s 是一個 Series 物件
type(s)
Out[78]:
In [79]:
# unstack: 將 row 旋轉為 column
# Series會變成一個 DataFrame
s.unstack()
Out[79]:
In [80]:
# 默認情況下,stack, unstack 操作的是最內層
# 可以傳入分層級別的編號或者名噌,以對其他級別操作
s.unstack(0)
Out[80]:
In [81]:
s.unstack('state')
Out[81]:
In [82]:
# 如果不是所有的級別值都可以在分組中找到的話,則unstack操作可以會產生缺失數據
s1 = Series([0, 1, 2, 3], index = list('abcd'))
s2 = Series([4, 5, 6], index = list('cde'))
data2 = pd.concat([s1, s2], keys = ['one', 'two'])
data2
Out[82]:
In [83]:
# unstack操作可以會產生缺失數據
data2.unstack()
Out[83]:
In [84]:
# stack 預設會濾除缺失數據,因此 stack/unstack 是可逆的
data2.unstack().stack()
Out[84]:
In [85]:
# 也可以設定 dropna 參數,不要濾除缺失數據
data2.unstack().stack(dropna = False)
Out[85]:
In [86]:
# unstack操作中,旋轉軸的級別將會成為結果中的最低級別
df = DataFrame({'left': s, 'right': s + 5},
columns = pd.Index(['left', 'right'], name = 'side'))
df
Out[86]:
In [87]:
# 索引'state'經過unstack之後,成為最內層的 column索引
df.unstack('state')
Out[87]:
In [88]:
df.unstack('state').unstack('side')
Out[88]:
In [89]:
df
Out[89]:
In [90]:
df.unstack('number').unstack('state')
Out[90]:
In [91]:
# 重新設定 ldata_string,不用依靠檔案載入
ldata_string = """
{"date":{"0":"1959\\/3\\/31","1":"1959\\/3\\/31","2":"1959\\/3\\/31","3":"1959\\/6\\/30","4":"1959\\/6\\/30","5":"1959\\/6\\/30","6":"1959\\/9\\/30","7":"1959\\/9\\/30","8":"1959\\/9\\/30"},"item":{"0":"realgdp","1":"infl","2":"unemp","3":"realgdp","4":"infl","5":"unemp","6":"realgdp","7":"infl","8":"unemp"},"value":{"0":2710.349,"1":0.0,"2":5.8,"3":2712.349,"4":2.0,"5":7.8,"6":2714.349,"7":4.0,"8":9.8}}
"""
In [92]:
import json
df = DataFrame(json.loads(ldata_string))
df
# 長格式
# 好處: 值的種類可以隨時增加或減少
# 缺點: 操作起來較麻煩,不易閱讀
Out[92]:
In [93]:
# pivot()方法 可以將 長格式 轉換為 寬格式
# 總共需要 index, columns, values 三個參數
pivoted = df.pivot(index = 'date', columns = 'item', values = 'value')
pivoted
Out[93]:
In [94]:
# 增加一列 value2
df['value2'] = np.random.randn(len(df))
df
Out[94]:
In [95]:
# 如果只指定 index, columns,則DataFrame就會具有層次化的columns
pivoted = df.pivot(index = 'date', columns = 'item')
pivoted
Out[95]:
In [96]:
pivoted['value'][:3]
Out[96]:
In [97]:
# 也可以用 set_index建立層次化的索引,然後再用 unstack建置
df.set_index(['date', 'item'])
Out[97]:
In [98]:
df.set_index(['date', 'item']).unstack('item')
Out[98]:
In [99]:
data = DataFrame({'k1': ['one'] * 3 + ['two'] * 4,
'k2': [1, 1, 2, 3, 3, 4, 4,]})
data
Out[99]:
In [100]:
# DataFrame 的 duplicated()方法傳回一個 boolean型態的 Series,表示各row是否重複
data.duplicated()
Out[100]:
In [101]:
# drop_duplicates()方法 傳回移除重複項目之後的結果
data.drop_duplicates()
Out[101]:
In [102]:
data['k3'] = range(7)
data
Out[102]:
In [103]:
# drop_duplicates()預設會對所有的columns來判斷是否有重複的 rows
data.duplicated()
Out[103]:
In [104]:
# 也可以針對指定的columns來判斷是否有重複的 rows
data.duplicated(['k1'])
Out[104]:
In [105]:
# duplicated, drop_duplicates 預設保留第一個出現的值組合
# 設定參數 keep = 'last',則會改為保留最後一個出現的值組合
data.duplicated(['k1'], keep = 'last')
Out[105]:
In [106]:
data = DataFrame({'food':['bacon', 'pulled pork', 'bacon', 'Pastrami', 'corned beef', 'Bacon', 'pastrami', 'honey ham', 'nova lox'],
'ounces': [4, 3, 12, 6, 7.5, 8, 3, 5, 6]})
data
Out[106]:
In [107]:
meat_to_animal = {'bacon': 'pig',
'pulled pork': 'pig',
'pastrami': 'cow',
'corned beef': 'cow',
'honey ham': 'pig',
'nova lox': 'salmon'}
In [108]:
# Series 的 map()方法,可以將元素map給特定的 字典或函數 來進行轉換
# 需先規整大小寫,也是透過 map 對每個元素做 str.lower的操作
data['animal'] = data['food'].map(str.lower).map(meat_to_animal)
data
Out[108]:
In [109]:
# 也可以透過 lambda來做
data['animal'] = data['food'].map(lambda x: meat_to_animal[x.lower()])
data
# 使用 map()是實現元素級清理與轉換的便捷方式
Out[109]:
In [110]:
data = Series([1., -999., 2., -999., -1000., 3.,])
data
Out[110]:
In [111]:
# 用 replace()方法來置換數值
data.replace(-999, np.nan)
Out[111]:
In [112]:
# 一次置換多個值。要被替換的包裝在一個list中
data.replace([-999, -1000], np.nan)
Out[112]:
In [113]:
# 對不同值 進行不同的替換
# 要被替換的放在第一個 list, 替換者放在第二個 list,要匹配
data.replace([-999, -1000], [np.nan, 0])
Out[113]:
In [114]:
# 替代關係用 dict表達會比較清楚
data.replace({-999: np.nan, -1000: 0})
Out[114]:
軸標籤也可以進行轉換,或者就地修改
In [115]:
data = DataFrame(np.arange(12).reshape((3, 4)),
index = pd.Index(['Ohio', 'Colorado', 'New York'], name = 'state'),
columns = pd.Index(['one', 'two', 'three', 'four'], name = 'quarter'))
data
Out[115]:
In [116]:
# Index 也有一個 map()方法,可以傳回一個新的 Index物件
data.index = data.index.map(str.upper)
data
Out[116]:
In [117]:
# rename()方法會傳回一個數據集的轉換版本,而不是修改原來的數據
# 使用 index, columns 指定的函式 來修改軸標籤
data.rename(index = str.title, columns = str.upper)
Out[117]:
In [118]:
# rename 可以結合字典型物件,實現對部分軸標籤的更新
data.rename(index = {'OHIO': 'INDIANA'}, columns = {'three': 'peekaboo'})
Out[118]:
In [119]:
data
Out[119]:
In [120]:
# 如果希望就地修改原有的數據集,使需要在 rename()方法中設定參數 inplace = True
data.rename(index = {'OHIO': 'INDIANA'}, columns = {'three': 'peekaboo'}, inplace = True)
data
Out[120]:
In [121]:
# 常態分布陣列
np.random.seed(12345)
df = DataFrame(np.random.randn(1000, 4))
df.describe()
Out[121]:
In [122]:
# 找出某列中,絕對值大於3的數值
col = df[2]
col[np.abs(col) > 3]
Out[122]:
In [123]:
# 找出所有 含有絕對值大於3的數值 的row,可以運用 any()
df[(np.abs(df) > 3).any(axis = 1)]
Out[123]:
In [124]:
# 將陣列數值限制在 +-3之間
gt3 = (np.abs(df) > 3)
df[gt3] = np.sign(df) * 3
df.describe()
Out[124]:
In [125]:
data = Series({'Dave': 'dave@google.com',
'Steve': 'steve@gmail.com',
'Rob': 'rob@gmail.com',
'Wes': np.nan})
data
Out[125]:
In [126]:
# 透過 Series 的 str屬性 可以訪問一些字串的方法
data.str.contains('gmail')
Out[126]:
In [127]:
# 是一個 StringMethods物件,之下掛了很多字串方法
data.str
Out[127]:
In [128]:
# .str 之下也掛有 reqular expression 的一些方法
pattern = r'([A-Z0-9._%+-]+)@([A-Z0-9.-]+)\.([A-Z]{2,4})'
# reg 的 findall()方法
data.str.findall(pattern, flags = re.IGNORECASE)
Out[128]:
In [129]:
# reg 的 match()方法
matchs = data.str.match(pattern, flags = re.IGNORECASE)
matchs
Out[129]:
In [130]:
# 提取匹配結果中 索引為 1 的元素
matchs.str.get(1)
Out[130]:
In [131]:
matchs.str[0]
Out[131]:
In [132]:
# 對字串進行子串擷取
data.str[:5]
Out[132]: